题解 P2325 【[SCOI2005]王室联邦】

题意:把一个有$n$个点的树划分成若干个,要求每个省至少要有$B$个城市,最多可以有$3B$个城市。

我们可以考虑这样一种构造方法:

我们$dfs$整棵树,处理当前节点$u$时,将其子节点进行分块,如果需要进行分块的节点数不能整除块的大小,则将未被分块的子节点与$u$分在同一个块。

枚举$u$的每个子节点$v$,递归处理子树后,将每个子节点返回的未被分块的节点添加到集合$S$中,一旦$\vert S\vert\geqslant size$就把$S$作为一个新的块并将$u$作为省会,然后清空$S$并继续枚举$v$。

处理完所有子树后,将$u$也加入到集合$S$中,此时在$S$中的节点为未被分块的节点,将被返回到上一层,显然$S$的大小最大为$size$,$size-1$个子树节点$+u$节点本身。

由于返回的集合的大小最大为$size$,对于一个子树会对集合最多增加$B-1$个节点,那么每个块的大小最大为$2B-1,$满足条件。

在$dfs$结束后,集合$S$中可能还有节点(最多有$B$个),那么我们把这$ B$个节点并入最后一个块(以根节点为省会的最后一个块,块的大小最大为 $2B-1$)中,那么这个块的大小最大为$3B-1$,符合条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
#define int long long
#define re register
#define inf 0x3f3f3f3f
#define N 100007
using namespace std;
struct node{
int to,next;
}e[N];
inline int read(){
int x=0,w=0;char ch=getchar();
while (!isdigit(ch))w|=ch=='-',ch=getchar();
while (isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return w?-x:x;
}
int cnt,top,head[N],sh[N],K,col[N],st[N],n,B;
inline void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int u,int fa){
int now=top;
for (int i=head[u];i;i=e[i].next){
int v=e[i].to;
if (v==fa)continue;
dfs(v,u);
if (top-now>=B){
sh[++K]=u;
while (top>now)col[st[top--]]=K;
}
}
st[++top]=u;
}
signed main(){
n=read(),B=read();
for (int i=1;i<n;++i){
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs(1,0);
if (n==B)sh[++K]=1;
while (top)col[st[top--]]=K;
printf("%d\n",K);
for (int i=1;i<=n;++i)printf("%d ",col[i]);puts("");
for (int i=1;i<=K;++i)printf("%d ",sh[i]);
return 0;
}